<template>
  <div class="merge-tree">
    <div class="merge-tree-header g-inline-flex">
      <div>
        <img
          class="merge-tree-icon"
          src="statics/icons/app/iconspread.svg"
          @click="expandAll"
        />
        <img
          class="merge-tree-icon"
          src="statics/icons/app/iconshrink.svg"
          @click="collapseAll"
        />
      </div>
    </div>
    <m-tree
      class="merge-tree-container"
      :treeData="[treeData.root]"
      :load-nodes="loadNodes"
      :node-key="'key'"
      lazy
      custom-load-result
    >
      <template #default="{ nodeData }">
        <span
          class="g-pointer g-m-tb-1"
          @click="e => handleClickNode(nodeData, e)"
          @dblclick="handleDbClick(nodeData)"
          @contextmenu="handleContextMenu(nodeData, $event)"
        >
          <template v-if="showAcceptIcon(nodeData)">
            <img
              v-if="nodeData.data.isAccept === IsAccept.NOT_ACCEPT"
              src="statics/icons/projectMerge/reject.svg"
              style="height: 16px; margin-right: 4px"
            />
            <img
              v-else-if="nodeData.data.isAccept === IsAccept.ACCEPT"
              src="statics/icons/projectMerge/accept.svg"
              style="height: 16px; margin-right: 4px"
            />
          </template>

          <img
            v-if="nodeData.data.move === Move.OUT"
            src="statics/icons/projectMerge/out.svg"
            style="height: 16px; margin-right: 4px"
          />
          <img
            v-else-if="nodeData.data.move === Move.IN"
            src="statics/icons/projectMerge/in.svg"
            style="height: 16px; margin-right: 4px"
          />
          <img
            :src="nodeData.data.icon"
            style="height: 16px"
          />
          <m-node-label
            v-if="nodeData.data"
            :nodeData="nodeData.data"
            :selected="isNodeSelected(nodeData)"
            :disabled="!!nodeData.disabled"
          />
        </span>
      </template>
    </m-tree>
    <merge-tree-background
      class="merge-tree-background"
      :tree-data="[treeData.root]"
    ></merge-tree-background>
  </div>
</template>

<script lang="ts" setup>
import { inject, onMounted, ref, watch } from "vue";
import MTree from "@/common/Tree/MTree.vue";
import MNodeLabel from "@/modules/model/comps/station/sider/modelTree/modelTree/NodeLabel.vue";
import {
  MergeTree,
  MergeModelData,
  BuildFakeData,
  Status,
  IsAccept,
  Move,
  NodeBackground,
  ProjectType,
  SelectedNode,
  MenuDisableConfig
} from "./logic";
import { projectService, modelService } from "../../../service";
import ContextMenu from "@/common/ContextMenu/index.vue";
import { contextMenuItem, PropertyValue, MenuKey, MergeProperty } from "../config";
import MergeTreeBackground from "./MergeTreeBackground.vue";
import { modelUtil } from "@/modules/model/util/ModelUtil";
import { MTreeNode } from "@/common/Tree/MTreeNode";
import { BusEvent, ProjectMergeStatus } from "@/constants/Enums";
import { Shape, ShapeType } from "@/mdesign-lib/mdesign-graph.umd.js";
import { useEvents } from "@/common/useEvents";
import { ModelData, ModelMergeInfo } from "@/constants/types";
import { StType } from "@/constants/StType";
import { cloneDeep } from "lodash-es";
import { loading } from "@/common/Loading";

/**
 * 名词说明：
 *  接受：接受模型的增加,删除,移入,移出，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *  拒绝：拒绝模型的增加,删除,移入,移出，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *  带属性接受：接受模型的增加,删除,移入,移出，以及所有属性变更，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *  带属性拒绝：接受模型的增加,删除,移入,移出，以及所有属性变更，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *  递归接收：递归接收当前节点以及子节点的增加,删除,移入,移出，以及所有属性变更，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *  递归拒绝：递归拒绝当前节点以及子节点的增加,删除,移入,移出，以及所有属性变更，如果是视图 则也接受视图图形的变更，以及视图内模型的变更
 *
 * 字段说明：
 *  status:
 *    UNMODIFIED：模型属性无变更
      ADD：新增的模型,
      DELETED: 删除的模型,
      MODIFIED： 模型属性存在变更,如果是视图 也可能是视图图形有变更

 *  innerChange: 内部有模型存在属性变更或内部有视图图形的变更

    move:
      NOTHING: 未移动
      OUT:节点被移出
      IN: 节点被移入

 *
 *
 */
const targetProjectId = inject("targetProjectId") as string;
const sourceProjectId = inject("sourceProjectId") as string;
const isSourceOnline = inject("isSourceOnline") as string;
const rootId = inject("rootId") as string;

const props = defineProps<{
  modelId: string;
  ownerId: string;
  updatedIds: string[];
  currentKey: string;
}>();

const colorConfigMap = {
  ADD: "#CFE5AB",
  DELETE: "#FCD3D8",
  MODIFIED: "#C5E2FD"
};

const initRoot = new BuildFakeData("");
const mergeTree = new MergeTree<MergeModelData>(initRoot, "", "");
const treeData = ref<MergeTree<MergeModelData>>(mergeTree);
const propertyList = ref<Array<MergeProperty>>([]);
const selectedNodes = ref<SelectedNode[]>([
  {
    id: rootId,
    ownerId: "",
    key: ""
  }
]);
const selectedId = ref(rootId);
const selectedOwnerId = ref();
const statusMap = ref<NodeBackground[]>([]);
const emits = defineEmits<{
  (event: "update:modelId", modelId: string, ownerId: string, isDisabled: boolean, res?: MergeProperty[]): void;
  (event: "update:property", modifyModels: string[]): void;
  (event: "openDiagram", modelId: string, data: MergeModelData): void;
  (event: "updateButtonList", menuConfig): void;
  (event: "multiSelect"): void;
  (event: "updateSelectedNodes", nodes: SelectedNode[]): void;
}>();

watch(
  () => props.updatedIds,
  async (newVal: string[]) => {
    if (newVal.length > 0) {
      await treeData.value.updateNode(newVal, sourceProjectId, targetProjectId);
      // 虽然configMenu有单独的watch，但是这里updateNode会将node属性更改，没有触发configMenu
      if (newVal.indexOf(selectedId.value) > -1 && !props.currentKey) {
        await configMenu(treeData.value.getNode(selectedId.value + "." + selectedOwnerId.value));
      }
    }
  }
);
// selectedId只监听modelId的变化，单机双击右键操作,先调用update:modelId改变modelId
watch(
  () => props.modelId,
  newVal => {
    selectedId.value = newVal;
  }
);

watch(
  () => props.ownerId,
  newVal => {
    selectedOwnerId.value = newVal;
  }
);

watch(
  () => props.currentKey,
  async newVal => {
    if (!newVal) {
      await configMenu(treeData.value.getNode(props.modelId + "." + props.ownerId));
    }
  }
);

const events = createEvents();
useEvents(events);

function createEvents() {
  const event = {
    [BusEvent.PROJECT_MERGE_SELECTED]: async (shape: Shape) => {
      let projectId = shape.projectMergeStatus === ProjectMergeStatus.ADDED ? sourceProjectId : targetProjectId;
      const path: ModelData[] = await modelService.getNodePath(projectId, shape.modelId);
      const currentModel: ModelData = path[path.length - 1];
      // 获取到路径后展示所有节点
      for (let index = 0; index < path.length; index++) {
        const model = path[index];
        const node = treeData.value.getNodeFromShape(model.id);
        if (node && node.data.hasChildNode && !node.expand) {
          await loadNodes(node);
          node.expand = true;
        }
        if (shape.shapeType === ShapeType.Edge && node && currentModel.ownerId === node.data.id) {
          const groupNode = treeData.value.getNode(currentModel.ownerId + "-relations");
          if (groupNode) {
            groupNode.expand = true;
          }
        }
      }
      const node2 = treeData.value.getNodeFromShape(shape.modelId);
      if (node2) {
        handleClickNode(node2);
      }
    }
  };
  return event;
}

function showAcceptIcon(node: MTreeNode<MergeModelData>) {
  if (node.data.status === Status.UNMODIFIED) {
    return false;
  } else if (
    node.data.status === Status.ADD ||
    node.data.status === Status.DELETED ||
    node.data.move === Move.IN ||
    node.data.move === Move.OUT
  ) {
    return true;
  } else if (node.data.status === Status.MODIFIED) {
    if (node.data.baseSt && modelUtil.isStDiagram(node.data.baseSt)) {
      return true;
    } else {
      return false;
    }
  } else {
    return false;
  }
}
// function showNotAcceptIcon() {

// }
async function handleClickNode(node: MTreeNode<MergeModelData>, e?: MouseEvent) {
  if (e?.ctrlKey || e?.shiftKey) {
    if (selectedNodes.value.findIndex(it => it.id === node.id) > -1) {
      const index = selectedNodes.value.findIndex(it => it.id === node.id);
      selectedNodes.value.splice(index, 1);
    } else {
      selectedNodes.value.push({
        id: node.id,
        ownerId: node.data.ownerId,
        key: node.key || ""
      });
    }
  } else {
    selectedNodes.value = [];
    selectedNodes.value.push({
      id: node.data.id,
      ownerId: node.data.ownerId,
      key: node.key || ""
    });
  }
  selectedOwnerId.value = node.data.ownerId;
  if (selectedNodes.value.length > 1) {
    emits("multiSelect");
  } else {
    if (node.key && node.key.indexOf("relations") < 0) {
      const res = await projectService.projectMergeQueryPropertyDiff(targetProjectId, sourceProjectId, node.data.id);
      propertyList.value = res.data;
    } else {
      propertyList.value = [];
    }
    emits("update:modelId", node.data.id, node.data.ownerId, !!node.disabled, propertyList.value);
  }
  emits("updateSelectedNodes", selectedNodes.value);
}
// 双击节点，如果是基础图就打开预览，如果不是则展示变更信息(单击效果一样)
async function handleDbClick(node: MTreeNode<MergeModelData>) {
  await handleClickNode(node);
  if (node.data.baseSt && modelUtil.isStDiagram(node.data.baseSt)) {
    emits("openDiagram", node.data.id, node.data);
  }
}
async function handleContextMenu(node: any, event: MouseEvent) {
  if (selectedNodes.value.length > 1) {
    if (selectedNodes.value.findIndex(it => it.key === node.key) < 0) {
      selectedNodes.value = [];
      selectedNodes.value.push({
        id: node.id,
        ownerId: node.data.ownerId,
        key: node.key || ""
      });
      selectedOwnerId.value = node.data.ownerId;
      const res = await projectService.projectMergeQueryPropertyDiff(targetProjectId, sourceProjectId, node.data.id);
      propertyList.value = res.data;
      emits("update:modelId", node.data.id, node.data.ownerId, !!node.disabled, propertyList.value);
    }
  } else {
    await handleClickNode(node);
  }
  emits("updateSelectedNodes", selectedNodes.value);
  event.stopPropagation();
  if (node.disabled) return;

  const menu = await configMenu(node);
  ContextMenu.popup({
    x: event.clientX,
    y: event.clientY,
    menuItems: menu,
    modelId: node.id
  })
    .then(async (menuItem: any) => {
      const timeout = setTimeout(() => {
        loading.popup("正在修改中");
      }, 1000);
      let mergeInfo = selectedNodes.value.map(it => {
        const param = {
          instanceId: it.id,
          ownerId: it.ownerId
        } as ModelMergeInfo;
        return param;
      });
      if (node.key.indexOf("relations") > -1) {
        mergeInfo = node.children.map(it => {
          const param = {
            instanceId: it.id,
            ownerId: it.data.ownerId
          };
          return param;
        });
      }
      const res = await projectService.projectMergeUpdateSingle(targetProjectId, sourceProjectId, menuItem.key, mergeInfo).finally(() => {
        clearTimeout(timeout);
        loading.close();
      });
      const instanceIds = res.data.modifyModel as string[];
      if (instanceIds.length > 0) {
        emits("update:property", instanceIds);
      }
    })
    .catch((e: any) => {
      e && console.error(e);
    });
}

async function loadNodes(node: any) {
  await treeData.value.expandNode(node);
  getBackgroundList();
  return;
}

function isNodeSelected(node: MTreeNode<MergeModelData>) {
  const selectedNode = selectedNodes.value.find(it => it.id === node.id && it.ownerId === node.data.ownerId);
  if (selectedNode) {
    return true;
  } else {
    return false;
  }
}

function getBackgroundList() {
  const statusArray = Array.from(treeData.value.nodeMap.values());
  const backgroundList: NodeBackground[] = [];
  statusArray.forEach(it => {
    const param = {
      id: it.data.id,
      status: it.data.status,
      background: colorConfigMap[it.data.status]
    };
    backgroundList.push(param);
  });
  statusMap.value = backgroundList;
}

async function configSingleMenu(node: MTreeNode<MergeModelData> | null | undefined) {
  if (!node) {
    return;
  }
  const menu = cloneDeep(contextMenuItem);
  const baseSt = node.data.baseSt as StType;

  const acceptMenu = menu.find(it => it.key === MenuKey.accept)!;
  const rejectMenu = menu.find(it => it.key === MenuKey.reject)!;

  // 接收拒绝菜单禁用逻辑
  if (node.data.move === Move.IN || node.data.move === Move.OUT) {
    acceptMenu.disabled = node.data.isAccept === IsAccept.ACCEPT;
    rejectMenu.disabled = node.data.isAccept !== IsAccept.ACCEPT;
  } else if (node.data.status === Status.UNMODIFIED) {
    acceptMenu.disabled = true;
    rejectMenu.disabled = true;
  } else if (node.data.status === Status.ADD || node.data.status === Status.DELETED) {
    acceptMenu.disabled = node.data.isAccept === IsAccept.ACCEPT;
    rejectMenu.disabled = node.data.isAccept !== IsAccept.ACCEPT;
  } else if (node.data.status === Status.MODIFIED) {
    if (modelUtil.isStDiagram(baseSt)) {
      acceptMenu.disabled = node.data.isAccept === IsAccept.ACCEPT;
      rejectMenu.disabled = node.data.isAccept !== IsAccept.ACCEPT;
    } else {
      acceptMenu.disabled = true;
      rejectMenu.disabled = true;
    }
  }

  // 递归接收拒绝菜单禁用逻辑
  const acceptCascadeMenu = menu.find(it => it.key === MenuKey.acceptCascade)!;
  const rejectCascadeMenu = menu.find(it => it.key === MenuKey.rejectCascade)!;
  if (node.data.innerChange) {
    const allChildAccepted = false;
    const allChildRejected = false;

    if (node.isLeaf) {
      acceptCascadeMenu.disabled = true;
      rejectCascadeMenu.disabled = true;
    } else {
      acceptCascadeMenu.disabled = allChildAccepted;
      rejectCascadeMenu.disabled = allChildRejected;
    }
  } else {
    acceptCascadeMenu.disabled = true;
    rejectCascadeMenu.disabled = true;
  }

  // if (node.data.hasChildNode) {
  //   if (node.children.length > 0) {
  //     if (node.children.every(it => it.data.isAccept === IsAccept.NOT_ACCEPT)) {
  //       menu[5].disabled = true;
  //     } else if (node.children.every(it => it.data.isAccept === IsAccept.ACCEPT)) {
  //       menu[4].disabled = true;
  //     }
  //   } else {
  //     const res = await projectService.projectMergeQueryChildren(targetProjectId, sourceProjectId, node.data.id);
  //     if (res.data) {
  //       if (res.data.every(it => it.acceptState === IsAccept.ACCEPT)) {
  //         menu[4].disabled = true;
  //       } else if (res.data.every(it => it.acceptState === IsAccept.NOT_ACCEPT)) {
  //         menu[5].disabled = true;
  //       }
  //     }
  //   }
  // } else {
  //   // 如果没有子节点递归按钮都置灰
  //   acceptCascadeMenu.disabled = true;
  //   rejectCascadeMenu.disabled = true;
  // }
  // if (node.data.status === Status.MODIFIED && node.data.move === Move.NOTHING && !modelUtil.isStDiagram(baseSt)) {
  //   menu[0].disabled = true;
  //   menu[1].disabled = true;
  // } else {
  //   if (node.data.isAccept === IsAccept.ACCEPT) {
  //     menu[0].disabled = true;
  //   } else {
  //     menu[1].disabled = true;
  //   }
  // }

  const acceptPropertyMenu = menu.find(it => it.key === MenuKey.acceptWithAttribute)!;
  const rejectProertyMenu = menu.find(it => it.key === MenuKey.rejectWithAttribute)!;

  if (node.data.status === Status.UNMODIFIED) {
    acceptPropertyMenu.disabled = true;
    rejectProertyMenu.disabled = true;
  } else {
    let allPropertyList: Array<PropertyValue> = [];
    propertyList.value.forEach(it => {
      allPropertyList = allPropertyList.concat(it.sourcePropertyValue);
    });
    allPropertyList = allPropertyList.filter(it => it.state !== Status.UNMODIFIED);
    if (allPropertyList.every(it => it.accept)) {
      acceptPropertyMenu.disabled = true;
    }
    if (allPropertyList.every(it => !it.accept)) {
      rejectProertyMenu.disabled = true;
    }

    // const modifiedProperties = allPropertyList.filter(it.state === Status.UNMODIFIED);
  }

  // if (allPropertyList.every(it => it.state === Status.UNMODIFIED || it.accept)) {
  //   propertyStatus = 2;
  // } else if (allPropertyList.every(it => it.state === Status.UNMODIFIED || !it.accept)) {
  //   propertyStatus = 1;
  // }
  // // 对于modified和unmodified的节点，带属性拒绝和带属性接收是不会改变自身的isAccept状态
  // if (propertyStatus === 1 && (node.data.isAccept === IsAccept.NOT_ACCEPT || node.data.status === Status.MODIFIED || node.data.status === Status.UNMODIFIED)) {
  //   rejectProertyMenu.disabled = true;
  // } else if (propertyStatus === 2 && (node.data.isAccept === IsAccept.ACCEPT || node.data.status === Status.MODIFIED || node.data.status === Status.UNMODIFIED)) {
  //   acceptPropertyMenu.disabled = true;
  // }
  // if (node.data.hasChildNode) {
  //   if (node.children.length > 0) {
  //     if (node.children.every(it => it.data.isAccept === IsAccept.NOT_ACCEPT)) {
  //       menu[5].disabled = true;
  //     } else if (node.children.every(it => it.data.isAccept === IsAccept.ACCEPT)) {
  //       menu[4].disabled = true;
  //     }
  //   } else {
  //     const res = await projectService.projectMergeQueryChildren(targetProjectId, sourceProjectId, node.data.id);
  //     if (res.data) {
  //       if (res.data.every(it => it.acceptState === IsAccept.ACCEPT)) {
  //         menu[4].disabled = true;
  //       } else if (res.data.every(it => it.acceptState === IsAccept.NOT_ACCEPT)) {
  //         menu[5].disabled = true;
  //       }
  //     }
  //   }
  // } else {
  //   // 如果没有子节点递归按钮都置灰
  //   menu[5].disabled = true;
  //   menu[4].disabled = true;
  // }
  // 对关系聚集节点特殊处理
  if (node.data.id.endsWith("-relations")) {
    for (let i = 0; i < 6; i++) {
      menu[i].disabled = true;
    }
  }
  const menuDisabledConfig = {};
  menu.forEach(it => {
    menuDisabledConfig[it.key] = it.disabled;
  });
  return {
    menuDisabledConfig,
    menu
  };
}

async function configMenu(node: MTreeNode<MergeModelData> | null | undefined) {
  const menuConfig: MenuDisableConfig[] = [];

  if (selectedNodes.value.length === 1) {
    if (node && node.id !== selectedNodes.value[0].id) return;
    const menuItem = await configSingleMenu(node);
    if (menuItem) {
      emits("updateButtonList", menuItem?.menuDisabledConfig);
      return menuItem?.menu;
    }
  } else {
    for (let selectNode of selectedNodes.value) {
      const tNode = treeData.value.getNode(selectNode.key);
      if (tNode) {
        const menuItem = await configSingleMenu(tNode);
        menuConfig.push(menuItem?.menuDisabledConfig as MenuDisableConfig);
      }
    }
    const multiMenuConfig = {
      accept: menuConfig.some(it => it.accept),
      acceptCascade: menuConfig.some(it => it.acceptCascade),
      acceptWithAttribute: menuConfig.some(it => it.acceptWithAttribute),
      reject: menuConfig.some(it => it.reject),
      rejectCascade: menuConfig.some(it => it.rejectCascade),
      rejectWithAttribute: menuConfig.some(it => it.rejectWithAttribute)
    };
    const menu = cloneDeep(contextMenuItem);
    const acceptMenu = menu.find(it => it.key === MenuKey.accept)!;
    const rejectMenu = menu.find(it => it.key === MenuKey.reject)!;
    const acceptCascadeMenu = menu.find(it => it.key === MenuKey.acceptCascade)!;
    const rejectCascadeMenu = menu.find(it => it.key === MenuKey.rejectCascade)!;
    const acceptPropertyMenu = menu.find(it => it.key === MenuKey.acceptWithAttribute)!;
    const rejectProertyMenu = menu.find(it => it.key === MenuKey.rejectWithAttribute)!;
    acceptMenu.disabled = multiMenuConfig.accept;
    rejectMenu.disabled = multiMenuConfig.reject;
    acceptCascadeMenu.disabled = multiMenuConfig.acceptCascade;
    rejectCascadeMenu.disabled = multiMenuConfig.rejectCascade;
    acceptPropertyMenu.disabled = multiMenuConfig.acceptWithAttribute;
    rejectProertyMenu.disabled = multiMenuConfig.rejectWithAttribute;
    emits("updateButtonList", multiMenuConfig);
    return menu;
  }
}

async function collapseAll() {
  if (selectedId.value) {
    const key = selectedId.value + "." + selectedOwnerId.value;
    const node = treeData.value.getNode(key);
    if (node) {
      node.expand = false;
    }
  }
}

async function expandAll() {
  if (selectedId.value) {
    const key = selectedId.value + "." + selectedOwnerId.value;
    const node = treeData.value.getNode(key);
    if (node) {
      await loadNodes(node);
      node.expand = true;
    }
  }
}

onMounted(async () => {
  const rootNode = await modelService.getRootNode(targetProjectId);
  const mergeRootNode = {
    ...rootNode,
    isAccept: IsAccept.NOTHING,
    status: Status.UNMODIFIED,
    hasChildNode: true,
    mergeFrom: targetProjectId,
    innerChange: false,
    key: rootNode.id + "." + rootNode.ownerId,
    move: Move.NOTHING,
    readOnly: false
  };
  const diffTree = new MergeTree<MergeModelData>(mergeRootNode, sourceProjectId, targetProjectId);
  treeData.value = diffTree;
  await treeData.value.updateNode([rootNode.id], sourceProjectId, targetProjectId);
  treeData.value.isSourceOnline = isSourceOnline === "online" ? ProjectType.ONLINE : ProjectType.NORMAL;
  if (treeData.value.isSourceOnline === ProjectType.ONLINE) {
    const sourceRootNode = await modelService.getRootNode(sourceProjectId);
    treeData.value.onlineSourceRootId = sourceRootNode.id;
    await treeData.value.loadPkgStauts();
  }
  getBackgroundList();
  handleClickNode(treeData.value.root);
  await expandAll();
});
</script>

<style lang="scss" scoped>
@import "@/assets/css/theme.scss";
.tree-node-background {
  width: 610px;
  height: 18px;
}
.tree-node-background-container {
  position: absolute;
  z-index: 0;
}
.merge-tree {
  position: relative;
  &-container {
    position: absolute;
    z-index: 1;
    padding: 10px;
  }
  &-header {
    background: $frame-siderbar-bg-color;
    height: 28px;
    width: 100%;
    align-items: center;
    padding: 0 5px;
  }
  &-icon {
    cursor: pointer;
    margin-left: 5px;
  }
  &-background {
    margin-top: 10px;
  }
}
</style>
